package com.cn.scenario.service.impl;


import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.cn.scenario.entity.AppUser;
import com.cn.scenario.entity.Product;
import com.cn.scenario.mapper.AppUserMapper;
import com.cn.scenario.service.AppUserService;
import com.cn.scenario.util.managethread.RefreshBloomThread;
import com.google.common.base.Charsets;
import com.google.common.hash.BloomFilter;
import com.google.common.hash.Funnels;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RBloomFilter;
import org.redisson.api.RMap;
import org.redisson.api.RedissonClient;
import org.redisson.codec.JsonJacksonCodec;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;
import java.util.stream.Collectors;

/**
 * @Author Boolean
 * @Date 2022/12/22 17:15
 * @Version 1.0
 */
@Service
@Slf4j
public class AppUserServiceImpl extends ServiceImpl<AppUserMapper, AppUser> implements AppUserService {
    private static final String BLOOM_STR_APPUSER = "appuser_list_bloom";

    private static final String REDIS_CACHE_APPUSER = "appuser_list";

    @Resource
    RedissonClient redissonClient;

    BloomFilter<String> bloomFilterAppUser = BloomFilter.create(Funnels.stringFunnel(Charsets.UTF_8), 100000, 0.01);


    /**
     * 启动时候将产品加入到 布隆过滤器中
     */
    //@PostConstruct
    public void init() throws InterruptedException, ExecutionException {
        this.refreshBloom();
    }

    /**
     * @description: 结合业务，测试版在下面
     * @author: 任秀兴
     * @date: 2022/12/25 15:07
     * @param: []
     * @return: void
     **/
    @Override
    public void refreshBloom() throws InterruptedException, ExecutionException {
        long beginTime = System.currentTimeMillis();
        //开始查询分页
        int current = 1;
        int size = 50000;
        Page<AppUser> pageAppUserList = this.page(new Page<>(current, size), new LambdaQueryWrapper<AppUser>().select(AppUser::getId));
        while (pageAppUserList.getRecords().size() > 0) {
            // 开始时间
            long start1 = System.currentTimeMillis();
            // 每500条数据开启一条线程
            int threadSize = 5000;
            // 总数据条数
            int dataSize = pageAppUserList.getRecords().size();
            // 线程数
            int threadNum = dataSize / threadSize + 1;
            // 定义标记,过滤threadNum为整数
            boolean special = dataSize % threadSize == 0;
            // 创建一个线程池
            ExecutorService exec = Executors.newFixedThreadPool(threadNum);
            // 定义一个任务集合
            List<Callable<Integer>> tasks = new ArrayList<Callable<Integer>>();
            Callable<Integer> task = null;
            CopyOnWriteArrayList<AppUser> cutList = null;
            final int[] num = {0};
            // 确定每条线程的数据
            for (int n = 0; n < threadNum; n++) {
                if (n == threadNum - 1) {
                    if (special) {
                        break;
                    }
                    cutList = new CopyOnWriteArrayList(pageAppUserList.getRecords().subList(threadSize * n, dataSize).toArray());
                } else {
                    cutList = new CopyOnWriteArrayList(pageAppUserList.getRecords().subList(threadSize * n, threadSize * (n + 1)).toArray());
                }
                final CopyOnWriteArrayList<AppUser> listStr = cutList;
                task = new Callable<Integer>() {
                    @Override
                    public Integer call() throws Exception {
                        for (AppUser appUser : listStr) {
                            //System.out.println(Thread.currentThread().getName() + "--appUser.getId():" + appUser.getId());
                            bloomFilterAppUser.put(appUser.getId());
                        }
                        return 1;
                    }
                };
                // 这里提交的任务容器列表和返回的Future列表存在顺序对应的关系
                tasks.add(task);
            }
            System.out.println("tasksSize=" + tasks.size());
            List<Future<Integer>> results = exec.invokeAll(tasks);
            for (Future<Integer> future : results) {
                //System.out.println("results.size=" + results.size());
                System.out.println(future.get());
            }
            // 关闭线程池
            exec.shutdown();
            System.out.println("线程任务执行结束");
            System.err.println("执行任务消耗了 ：" + (System.currentTimeMillis() - start1) + "毫秒");
            pageAppUserList = this.page(new Page<>(++current, size), new LambdaQueryWrapper<AppUser>().select(AppUser::getId));
        }
        System.out.println("bloomFilterAppUser时间："+(System.currentTimeMillis()-beginTime)/1000+"秒");
    }


    /*@Override
    public void refreshBloom() {
        Long beginTime = System.currentTimeMillis();
        bloomFilterAppUser.delete();
        //初始化布隆过滤器：预计元素为 1000000L (这个值根据实际的数量进行调整),误差率为3%
        bloomFilterAppUser.tryInit(3000000L, 0.03);
        //开始查询分页
        int current = 1;
        int size = 100000;
        Page<AppUser> pageAppUserList = this.page(new Page<>(current, size),new LambdaQueryWrapper<AppUser>().select(AppUser::getId));
        while(pageAppUserList.getRecords().size()>0){
            // 优化二次
            pageAppUserList.getRecords().stream().parallel().forEach(a ->{
                bloomFilterAppUser.add(a.getId());
            });
            // 优化一次
            *//*List<Integer> list = pageAppUserList.getRecords().stream()
                    .map(AppUser::getId).collect(Collectors.toList());
            list.stream().parallel().forEach(a -> {
                bloomFilterAppUser.add(a);
            });*//*
            //第一次
            //list.forEach(bloomFilterAppUser::add);
            pageAppUserList = this.page(new Page<>(++current, size),new LambdaQueryWrapper<AppUser>().select(AppUser::getId));
        }
        System.out.println("bloomFilterAppUser时间："+(System.currentTimeMillis()-beginTime)/1000+"秒");
    }*/

    @Override
    public AppUser getAppUserById(String id) {
        // 走布隆过滤器筛选一下，防止被缓存穿透
        boolean contains = bloomFilterAppUser.mightContain(id);
        // 如果布隆过滤器判断当前产品id 存在，则去查询数据库
        if (contains) {
            RMap<String, String> appuserCache = redissonClient.getMap(REDIS_CACHE_APPUSER);
            String cacheAppUser = appuserCache.get(id);
            if (StrUtil.isNotEmpty(cacheAppUser)) {
                // 如果缓存中不是空 则返回
                return JSONUtil.toBean(cacheAppUser, AppUser.class);
            }
            AppUser appuser = this.getById(id);
            // 如果查到了数据，那么存一份到 redis 中去
            if (BeanUtil.isNotEmpty(appuser)) {
                appuserCache.put(id, JSONUtil.toJsonStr(appuser));
                return appuser;
            }
        } else {
            log.info("布隆过滤器中不存在人员id：{}的数据", id);
        }
        return null;
    }


    @Override
    public Boolean addAppUser(AppUser appuser) {
        boolean success = this.save(appuser);

        // 数据添加成功后，往布隆过滤器中添加数据
        if (success) {
            final String id = appuser.getId();
            bloomFilterAppUser.put(id);
        }
        return success;
    }


    /**
     * @description: 更新策略  先更新数据库，再删缓存
     * @author: 任秀兴
     * @date: 2023/1/3 10:53
     * @param: [appuser]
     * @return: java.lang.Boolean
     **/
    @Override
    public Boolean updateAppUser(AppUser appuser) {
        boolean success = false;
        String id = appuser.getId();
        boolean contains = bloomFilterAppUser.mightContain(id);
        if (contains) {
            success = this.updateById(appuser);
            if (success) {
                RMap<String, String> appuserCache = redissonClient.getMap(REDIS_CACHE_APPUSER);
                String cacheAppUser = appuserCache.get(id);
                if (StrUtil.isNotEmpty(cacheAppUser)) {
                    appuserCache.remove(id);
                }
            }
        } else {
            log.info("布隆过滤器中不存在人员id：{}的数据", id);
        }
        return success;
    }

}
